﻿using LinearFunctionFitting.Demo.Algorithm;
using ScottPlot;
using ScottPlot.WinForms;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace LinearFunctionFitting.Demo
{
    public partial class FrmMain : Form
    {
        readonly IFunction _ordinaryFunction;
        readonly IFunction _vectorFunction;
        public FrmMain()
        {
            InitializeComponent();
            _ordinaryFunction = new OrdinaryMethods();
            _vectorFunction = new VectorMethods();

            nudStartX.Value = StartX;
            nudEndX.Value = EndX;
        }
        public int StartX { get; set; } = -10;
        public int EndX { get; set; } = 10;
        private void nudStartX_ValueChanged(object sender, EventArgs e)
        {
            int val = (int)nudStartX.Value;
            if (val >= EndX)
                nudStartX.Value = StartX;
            else
            {
                StartX = val;
            }
        }
        private void nudEndX_ValueChanged(object sender, EventArgs e)
        {
            int val = (int)nudEndX.Value;
            if (val <= StartX)
                nudEndX.Value = EndX;
            else
            {
                EndX = val;
            }
        }
        int _fa;
        int _fb;
        private void btnRun_Click(object sender, EventArgs e)
        {
            int a = _rnd.Next(-5, 5);
            while (a == 0)
                a = _rnd.Next(-5, 5);
            int b = _rnd.Next(-10, 10);
            _fa = a; _fb = b;
            LoadData();
            InitPlot(formsPlot1);
            InitPlot(formsPlot2);

            Compute(_ordinaryFunction, formsPlot1, "普通方法拟合", lblOrdinaryResult, lblOrdinaryTime,lblOrdinary);
            Compute(_vectorFunction, formsPlot2, "向量拟合", lblVectorResult, lblVectorTime, lblVector);
        }
        private void Compute(IFunction function, ScottPlot.WinForms.FormsPlot formsPlot,
            string text, System.Windows.Forms.Label lblResult, System.Windows.Forms.Label lblTime, System.Windows.Forms.Label lblR)
        {
            Task.Factory.StartNew(() =>
            {
                Stopwatch sw = Stopwatch.StartNew();
                (int a, int b) = function.LinearFunction(_xs, _ys);
                sw.Stop();
                int[] ys = new int[_xs.Length];
                for (int i = 0; i < ys.Length; i++)
                {
                    ys[i] = a * _xs[i] + b;
                }
                double r = R(ys);
                this.ExecBeginInvoke(() =>
                {
                    var p = formsPlot.Plot.Add.Scatter(_xs, ys, color2);
                    p.LegendText = text;
                    p.MarkerSize = 5;
                    formsPlot.Refresh();
                    lblResult.Text = $"y={a}x{(b >= 0 ? "+" + b : b.ToString())}";
                    lblTime.Text = sw.ElapsedMilliseconds.ToString();
                    lblR.Text = r.ToString("f2");
                });
            });
        }
        private double R(int[] ys)
        {
            double avg = _ys.Average();
            double ssTot = 0d;
            double ssRes = 0d;
            for (int i = 0; i < ys.Length; i++)
            {
                double t = _ys[i] - avg;
                ssTot += t * t;
                double r = _ys[i] - ys[i];
                ssRes += r * r;
            }
            return 1 - ssRes / ssTot;
        }
        readonly ScottPlot.Color color = new ScottPlot.Color(255, 0, 0);
        readonly ScottPlot.Color color2 = new ScottPlot.Color(0, 0, 255);
        private void InitPlot(ScottPlot.WinForms.FormsPlot formsPlot)
        {
            formsPlot.Plot.Clear();

            var p = formsPlot.Plot.Add.ScatterPoints(_xs, _ys, color: color);
            p.LegendText = $"散点:" + $"y={_fa}x{(_fb >= 0 ? "+" + _fb : _fb.ToString())}";
            p.MarkerSize = 5;
            formsPlot.Plot.Axes.SetLimitsX(StartX, EndX);
            formsPlot.Plot.Font.Set("微软雅黑");
            formsPlot.Plot.XLabel("自变量");
            formsPlot.Plot.YLabel("因变量");
            formsPlot.Plot.Axes.SetLimitsY(_ys.Min() - 10, _ys.Max() + 10);
            formsPlot.Plot.Legend.IsVisible = true;
            formsPlot.Plot.ShowLegend(Alignment.LowerRight);
            formsPlot.Refresh();
        }
        int[] _xs;
        int[] _ys;
        readonly Random _rnd = new Random();
        const int _deviation = 3;
        private void LoadData()
        {
            _xs = new int[EndX - StartX];
            _ys = new int[EndX - StartX];
            int startX = StartX;
            for (int i = 0; i < _xs.Length; i++)
            {
                _xs[i] = startX;
                _ys[i] = TestFun(startX, _fa, _fb) + _rnd.Next(-_deviation, _deviation);
                startX++;
            }
        }
        private int TestFun(int x, int a, int b)
        {
            return x * a + b;
        }
    }
}
